<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>观察者模式（Observer Pattern）</title>
    <!-- 
  观察者模式（Observer Pattern）是一种行为型设计模式，它定义了一种一对多的依赖关系，使得当一个对象的状态发生变化时，所有依赖于它的对象都得到通知并自动更新。这个模式常用于实现事件处理系统和发布-订阅机制。

    ### 观察者模式的主要组成部分
    
    1. **主题（Subject）**：被观察的对象，维护一组观察者，并提供添加、删除观察者的方法。
    2. **观察者（Observer）**：定义一个更新接口，以便在主题状态变化时得到通知。
    3. **具体主题（Concrete Subject）**：实现主题接口，维护观察者的列表，并在状态变化时通知观察者。
    4. **具体观察者（Concrete Observer）**：实现观察者接口，定义在主题状态变化时的具体响应行为。

    ### 观察者模式的优点

    1. **松耦合**：观察者和主题之间的关系是松耦合的，观察者不需要知道主题的具体实现。
    2. **动态添加和删除观察者**：可以在运行时灵活地添加和删除观察者，增强了系统的灵活性。
    3. **简化了代码**：通过将观察者逻辑分离到不同的类中，简化了代码的结构。

    ### 观察者模式的缺点

    1. **可能导致过多的通知**：如果观察者数量较多，主题状态变化时可能会导致大量的通知，影响性能。
    2. **难以管理依赖关系**：在复杂系统中，观察者和主题之间的依赖关系可能会变得难以管理。

    ### 示例代码

    以下是一个使用观察者模式的简单示例，展示如何实现一个天气监测系统。

    #### 1. 主题接口（Subject）



    ``` javascript
    class Subject {
        constructor() {
            this.observers = []; // 观察者列表
        }

        addObserver(observer) {
            this.observers.push(observer);
        }

        removeObserver(observer) {
            this.observers = this.observers.filter(obs => obs !== observer);
        }

        notifyObservers(data) {
            this.observers.forEach(observer => observer.update(data));
        }
    }
    ``` 
    


    #### 2. 具体主题（Concrete Subject）



    ``` javascript
    class WeatherStation extends Subject {
        constructor() {
            super();
            this.temperature = 0;
        }

        setTemperature(temp) {
            this.temperature = temp;
            this.notifyObservers(this.temperature); // 通知观察者
        }
    }
    ``` 
    


    #### 3. 观察者接口（Observer）



    ``` javascript
    class Observer {
        update(data) {
            throw new Error("This method must be implemented.");
        }
    }
    ``` 
    


    #### 4. 具体观察者（Concrete Observer）



    ``` javascript
    class PhoneDisplay extends Observer {
        update(data) {
            console.log(`手机显示温度: ${data}°C`);
        }
    }

    class WebDisplay extends Observer {
        update(data) {
            console.log(`网页显示温度: ${data}°C`);
        }
    }
    ``` 
    


    #### 5. 使用示例



    ``` javascript
    const weatherStation = new WeatherStation();

    const phoneDisplay = new PhoneDisplay();
    const webDisplay = new WebDisplay();

    weatherStation.addObserver(phoneDisplay);
    weatherStation.addObserver(webDisplay);

    weatherStation.setTemperature(25); // 输出: 手机显示温度: 25°C
                                        // 输出: 网页显示温度: 25°C

    weatherStation.setTemperature(30); // 输出: 手机显示温度: 30°C
                                        // 输出: 网页显示温度: 30°C
    ``` 
    


    ### 总结

    观察者模式是一种有效的设计模式，适用于需要在对象状态变化时自动通知其他对象的场景。通过实现松耦合的观察者和主题关系，观察者模式提高了系统的灵活性和可维护性。在设计系统时，合理运用观察者模式可以有效地管理对象之间的依赖关系，增强系统的响应能力。
  --></head>

  <body>
    <script>
      // 1. 定义主题（Subject）接口, 触发每个观察者
      class Subject {
        constructor() {
          this.state = 0;
          this.ovservers = [];
        }
        getState() {
          return this.state;
        }

        setState(state) {
          this.state = state;
          this.notify();
        }
        attach(observer) {
          console.log(observer);

          this.ovservers.push(observer);
        }
        notify() {
          this.ovservers.forEach((observer) => observer.update(this));
        }
      }

      // 2. 定义观察者（Observer）接口
      class Observer {
        constructor(name, subject) {
          this.name = name;
          this.subject = subject;
          this.subject.attach(this);
        }
        update(subject) {
          console.log(`${this.name}观察者收到通知: ${subject.getState()}`);
        }
      }

      // 3. 使用示例
      const subject = new Subject();
      const observer1 = new Observer("观察者1", subject);
      const observer2 = new Observer("观察者2", subject);
      subject.setState(30); // 输出: 观察者1收到通知: 30, 观察者2收到通知: 30
      subject.setState(50); // 输出: 观察者1收到通知: 50, 观察者2收到通知: 50
    </script>
  </body>
</html>
